using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json.Linq;

namespace JsonRpcGen.TypeHandler
{
    internal class ObjectTypeHandler : TypeHandler
    {
        private readonly string _baseName;
        private readonly JToken _properties;

        public ObjectTypeHandler(JToken properties)
        {
            _properties = properties;
        }

        public ObjectTypeHandler(JToken properties, string baseName):this(properties)
        {
            _baseName = baseName;
        }


        public override void Write(StreamWriter writer, string namesp, string className, Dictionary<string, string> replacements)
        {
            if(namesp==null)
            {
                WriteClass(writer, className, replacements);
                return;
            }
            writer.WriteLine("namespace " + namesp);
            writer.WriteLine("{");
            WriteClass(writer, className, replacements);
            writer.WriteLine("}");
        }

        public void WriteClass(StreamWriter writer, string className, Dictionary<string, string> replacements, bool allowNestedClasses=true)
        {
            writer.Write("   public class " + className);
            WriteBody(writer, className, replacements, "public", allowNestedClasses);
            writer.WriteLine("   }");
        }
        public void WriteInterface(StreamWriter writer, string className, Dictionary<string, string> replacements)
        {
            writer.Write("   public interface " + className);
            WriteBody(writer, className, replacements, "", false);
            writer.WriteLine("   }");
        }

        private void WriteBody(StreamWriter writer, string className, Dictionary<string, string> replacements, string modifier, bool allowNestedClasses)
        {
            if (_baseName != null)
            {
                var baseName = Global.BaseNamespace + "." + _baseName;
                baseName = baseName.Replace(Global.BaseNamespace + "." + Global.BaseNamespace + ".",
                                              Global.BaseNamespace + ".");
                writer.Write(" : " + baseName);
            }
            writer.WriteLine();

            writer.WriteLine("   {");
            WriteProperties(writer, className, replacements, modifier, allowNestedClasses);
        }

        public void WriteNestedClasses(StreamWriter writer, Dictionary<string, string> replacements)
        {
            WriteProperties(writer,null,replacements,null,true,true);
        }

        private void WriteProperties(StreamWriter writer, string className, Dictionary<string, string> replacements, string modifier,
                                    bool allowNestedClasses, bool onlyClasses=false)
        {
            foreach (JProperty prop in _properties)
            {
                if (prop.First["type"] != null && prop.First["type"].ToString() == "object")
                {
                    var props = prop.First["properties"];
                    if (props != null)
                    {
                        var typehandler = new ObjectTypeHandler(prop.First["properties"]);
                        if (allowNestedClasses)
                        {
                            typehandler.WriteClass(writer, Global.GetTypeName(prop, className, replacements), replacements);
                        }
                    }
                }
                string typeName;

                if (prop.First["type"] != null && prop.First["type"].ToString() == "array")
                {
                    var name = prop.First["items"]["$ref"];
                    if (name != null)
                    {
                        typeName = Global.BaseNamespace + "." + name + "[]";
                    }
                    else
                    {
                        var typehandler = new ArrayTypeHandler(prop.First["items"]);
                        if (allowNestedClasses)
                        {
                            typehandler.WriteClass(writer, prop.Name + "Item", replacements);
                        }
                        typeName = prop.Name + "Item[]";
                    }
                }
                else if (prop.First["enums"] != null && prop.First["type"].ToString() == "string")
                {
                    var typehandler = new EnumTypeHandler(prop.First["enums"]);
                    if (allowNestedClasses)
                    {
                        typehandler.WriteEnum(writer, prop.Name + "Enum");
                    }
                    typeName = prop.Name + "Enum";
                }
                else if (prop.First["additionalProperties"] != null && prop.First["additionalProperties"].ToString() != "false")
                {
                    typeName = "global::System.Collections.Generic.Dictionary<string,string>";
                }
                else
                {
                    typeName = Global.GetTypeName(prop, className, replacements);
                }
                if (!onlyClasses)
                {
                    var propName = prop.Name;
                    if (Global.IsReservedName(propName))
                    {
                        writer.WriteLine("       [Newtonsoft.Json.JsonProperty(\"" + propName + "\")]");
                        propName = Global.MakeFirstUpper(propName);
                    }

                    writer.WriteLine("       " + modifier + " " + typeName + " " + propName + " {get;set;}");
                }
            }
        }
    }
}